﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using SuppKids.Core.Data;
using SuppKids.Core.Domain;

namespace SuppKids.Core.Repository
{
    public class EntityRepository<T> : BaseRepository<T> where T : Entity
	{
		public EntityRepository(IDataContext context)
			: base(context)
		{
		}

		public DbSet<T> DataSet
		{
			get { return ((IEntityDbContext)Context).Set<T>(); }
		}

		public override IQueryable<T> GetQuery()
		{
			return this.DataSet;
		}

		public override IEnumerable<T> GetAll(Expression<Func<T, bool>> whereCondition, params Expression<Func<T, object>>[] includes)
		{
			return this.Include(includes).Where(whereCondition);
		}

		public override IEnumerable<T> GetAll(params Expression<Func<T, object>>[] includes)
		{
			return this.Include(includes).ToList();
		}

		public override IEnumerable<T> GetPage<TKey>(int pageNumber, int pageSize, out int totalCount, Expression<Func<T, bool>> whereCondition, Expression<Func<T, TKey>> orderBy, bool orderByAscending = true, params Expression<Func<T, object>>[] includes)
		{
			var query = this.Include(includes).Where(whereCondition);
			totalCount = query.Count(whereCondition);
			query = orderByAscending ? query.OrderBy(orderBy) : query.OrderByDescending(orderBy);
			return query.Skip((pageNumber - 1) * pageSize).Take(pageSize);
		}

		public override IEnumerable<T> GetPage<TKey>(
			int pageNumber,
			int pageSize,
			out int totalCount,
			Expression<Func<T, bool>> whereCondition,
			Func<IQueryable<T>, IQueryable<T>> orderByClause = null,
			params Expression<Func<T, object>>[] includes)
		{
			var query = this.Include(includes).Where(whereCondition);
			totalCount = query.Count(whereCondition);
			if (orderByClause != null)
			{
				query = orderByClause(query);
			}

			return query.Skip((pageNumber - 1) * pageSize).Take(pageSize);
		}

		public override IEnumerable<T> GetPageOrderedById(int pageNumber, int pageSize, out int totalCount)
		{
			return this.GetPage(pageNumber, pageSize, out totalCount, a => true, a => a.Id);
		}

		public override T SingleOrDefault(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includes)
		{
			return this.Include(includes).SingleOrDefault(predicate);
		}

		public override T FirstOrDefault(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includes)
		{
			return this.Include(includes).FirstOrDefault(predicate);
		}

		public override T FirstOrDefault(int id)
		{
			return this.DataSet.FirstOrDefault(x => x.Id == id);
		}

		public override void Add(T entity)
		{
			this.DataSet.Add(entity);
		}

		public override void Delete(T entity)
		{
			if (entity is IDeletable)
			{
				var deletedHolder = entity as IDeletable;
				deletedHolder.IsDeleted = true;
			}
			else
			{
				this.DataSet.Remove(entity);
			}
		}

		public override void Attach(T entity)
		{
			this.DataSet.Attach(entity);
		}

		public override void Update(T entity)
		{
			if (entity.Id == default(int))
			{
				this.Add(entity);
			}
		}

		protected IQueryable<T> Include(params Expression<Func<T, object>>[] includes)
		{
			var result = this.GetQuery();
			if (includes != null)
			{
				result = includes.Aggregate(result, (current, include) => current.Include(include));
			}

			return result;
		}

		protected IQueryable<T> Include(params string[] includes)
		{
			var result = this.GetQuery();
			if (includes != null)
			{
				result = includes.Aggregate(result, (current, include) => current.Include(include));
			}

			return result;
		}
	}
}